#include "Precomp.h"
#include "SQGame.h"
#include "AllBindings.h"
#include "Map.h"
#include "Renderer.h"
#include "InputManager.h"
#include "Logger.h"
#include <set>

using namespace sf;

#define VERSION "0.1a"

SQGame* SQGame::instance=NULL;

SQGame::SQGame()
:window(),editMode(false),renderer(window),inputManager(window.GetInput())
{
	if(instance)
		throw std::exception("SQGame constructor is called more than once");
	instance=this;
}

SQGame::~SQGame()
{
	instance=NULL;
	Log("Game ended");
}

bool SQGame::init(int argc,char** argv,const SQChar* startUpScript)
{
	Log("Bull's Game Builder version " VERSION " started\n");
	Log("Using SFML version 1.4\n");
	SquirrelVM::Init();
	SquirrelVM::SetPrintFunc(SquirrelLog);
	//Script binding
	bindEntity();
	bindSFML();
	bindGame();
	bindInput();
	bindAddOn();
	//-------------
	Log("Reading configuration...\n");
	VideoMode mode;
	try
	{
		SquirrelObject script=SquirrelVM::CompileScript(startUpScript);
		SquirrelVM::RunScript(script);
		const SquirrelObject global=SquirrelVM::GetRootTable();
		mode.Width=global.GetInt(L"width");
		mode.Height=global.GetInt(L"height");
	}
	catch(SquirrelError& e)
	{
		Log(L"Squirrel error:%s\n",e.desc);
		return false;
	}
	Log("Initializing rendering window\n");
	window.Create(mode,"BGB " VERSION);
	return true;
}

void SQGame::setCurrentMap(Map* map)
{
	currentMap->onLeave();
	currentMap=map;
	currentMap->onEnter();
	Map::defaultMap=map;
	renderer.setMap(map);
}

bool SQGame::run(const SQChar* mainScript)
{
	Map defaultMap;
	setCurrentMap(&defaultMap);
	setDesiredFPS(60);
	Event ev;
	renderer.setMap(currentMap);

	std::set<wstring> ignore;//built-in class cannot be saved directly
	SquirrelObject root=SquirrelVM::GetRootTable();
	SquirrelObject key,value;
	root.BeginIteration();
	while(root.Next(key,value))
	{
		if((value.GetType()==OT_CLASS))
		{//build ignore list
			ignore.insert(key.ToString());
		}
	}
	root.EndIteration();

	try//loading the main script
	{
		SquirrelObject script=SquirrelVM::CompileScript(mainScript);
		SquirrelVM::RunScript(script);
		script.Reset();
	}
	catch(SquirrelError& e)
	{
		Log(L"Squirrel exception:%s\n",e.desc);
		//std::cin.get();
	}
	catch(std::exception& e)
	{
		Log("Standard exception: %s\n",e.what());
		/*std::cout<<"Rethrow?? (n):";
		char answer;
		std::cin.get(answer);
		if(answer=='y'||answer=='Y')
			throw;*/
	}
	
	//register all classes
	root=SquirrelVM::GetRootTable();
	root.BeginIteration();
	while(root.Next(key,value))
	{
		if((value.GetType()==OT_CLASS))
		{
			if(ignore.find(key.ToString())!=ignore.end())//ingore built-in class
				continue;
			SquirrelObject att=value.GetAttributes();//get class attribute
			if(att.IsNull())
			{//create the attribute table
				HSQUIRRELVM vm=SquirrelVM::GetVMPtr();
				int top = sq_gettop(vm);
				sq_pushobject(vm,value);
				sq_pushnull(vm);
				sq_newtable(vm);
				att.AttachToStackObject(-1);
				if(SQ_FAILED(sq_setattributes(vm,-3)))
				{
					Log("Squirrel Error:%s\n",SquirrelError().desc);
				}
				sq_settop(vm,top);				
			}
			att.SetValue(L"__classname",key.ToString());//set the attribute __classname to the ... class's name
			//editor.registerClass(key.ToString());
		}
	}
	root.EndIteration();
	//start main game loop
	try
	{
		while (window.IsOpened())
		{
			while(window.GetEvent(ev))
			{
				if(ev.Type==Event::Closed)
					window.Close();
				inputManager.onEvent(ev);
			}
			currentMap->update();
			window.Clear();
			renderer.render();
			window.Display();
		}
		return true;
	}

	catch(SquirrelError& e)
	{
		Log(L"Squirrel exception:%s\n",e.desc);
		/*std::cout<<"Rethrow?? (n):";
		char answer;
		std::cin.get(answer);
		if(answer=='y'||answer=='Y')
			throw;*/
	}

	catch(std::exception& e)
	{
		Log("Standard exception: %s\n",e.what());
		/*std::cout<<"Rethrow?? (n):";
		char answer;
		std::cin.get(answer);
		if(answer=='y'||answer=='Y')
			throw;*/
	}

	return false;
}

void SQGame::setEditMode(bool state)
{
	if(editMode&&state)
		return;
	editMode=state;

	if(editMode)
	{
		sf::Event ev;
		try
		{
			while (window.IsOpened())
			{
				while(window.GetEvent(ev))
				{
					if(ev.Type==Event::Closed)
						window.Close();
					editor.sendEvent(ev);
				}
				editor.update();
				//currentMap->update();
				window.Clear();
				renderer.render();
				window.Display();
			}
		}

		catch(SquirrelError& e)
		{
			Log(L"Squirrel exception:%s\n",e.desc);
			/*std::cout<<"Rethrow?? (n):";
			char answer;
			std::cin.get(answer);
			if(answer=='y'||answer=='Y')
				throw;*/
		}

		catch(std::exception& e)
		{
			Log("Standard exception: %s\n",e.what());
			/*std::cout<<"Rethrow?? (n):";
			char answer;
			std::cin.get(answer);
			if(answer=='y'||answer=='Y')
				throw;*/
		}
	}
}

void SQGame::setDesiredFPS(unsigned int FPS)
{
	window.SetFramerateLimit(FPS);
}

float SQGame::getFrameTime() const
{
	return window.GetFrameTime();
}